﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DiscoveryLogic.Common.Numeric
{
    [Serializable]
    public abstract class RandomEngine : PersistentObject, DoubleFunction, IntFunction
    {
        /// <summary> Makes this class non instantiable, but still let's others inherit from it.</summary>
        protected internal RandomEngine()
        {
        }
        /// <summary>Equivalent to <tt>raw()</tt>.
        /// This has the effect that random engines can now be used as function objects, returning a random number upon function evaluation.
        /// </summary>
        public virtual double apply(double dummy)
        {
            return raw();
        }
        /// <summary>Equivalent to <tt>nextInt()</tt>.
        /// This has the effect that random engines can now be used as function objects, returning a random number upon function evaluation.
        /// </summary>
        public virtual int apply(int dummy)
        {
            return nextInt();
        }
        /// <summary> Constructs and returns a new uniform random number engine seeded with the current time.
        /// Currently this is {@link cern.jet.random.engine.MersenneTwister}.
        /// </summary>
        public static RandomEngine makeDefault()
        {
            return new MersenneTwister((int)((System.DateTime.Now.Ticks - 621355968000000000) / 10000));
        }
        /// <summary> Returns a 64 bit uniformly distributed random number in the open unit interval <code>(0.0,1.0)</code> (excluding 0.0 and 1.0).</summary>
        public virtual double nextDouble()
        {
            double nextDouble;

            do
            {
                // -9.223372036854776E18 == (double) Long.MIN_VALUE
                // 5.421010862427522E-20 == 1 / Math.pow(2,64) == 1 / ((double) Long.MAX_VALUE - (double) Long.MIN_VALUE);
                //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
                nextDouble = ((double)nextLong() - (-9.223372036854776e18)) * 5.421010862427522e-20;
            }
            // catch loss of precision of long --> double conversion
            while (!(nextDouble > 0.0 && nextDouble < 1.0));

            // --> in (0.0,1.0)
            return nextDouble;

            /*
            nextLong == Long.MAX_VALUE         --> 1.0
            nextLong == Long.MIN_VALUE         --> 0.0
            nextLong == Long.MAX_VALUE-1       --> 1.0
            nextLong == Long.MAX_VALUE-100000L --> 0.9999999999999946
            nextLong == Long.MIN_VALUE+1       --> 0.0
            nextLong == Long.MIN_VALUE-100000L --> 0.9999999999999946
            nextLong == 1L                     --> 0.5
            nextLong == -1L                    --> 0.5
            nextLong == 2L                     --> 0.5
            nextLong == -2L                    --> 0.5
            nextLong == 2L+100000L             --> 0.5000000000000054
            nextLong == -2L-100000L            --> 0.49999999999999456
            */
        }
        /// <summary> Returns a 32 bit uniformly distributed random number in the open unit interval <code>(0.0f,1.0f)</code> (excluding 0.0f and 1.0f).</summary>
        public virtual float nextFloat()
        {
            // catch loss of precision of double --> float conversion
            float nextFloat;
            do
            {
                //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
                nextFloat = (float)raw();
            }
            while (nextFloat >= 1.0f);

            // --> in (0.0f,1.0f)
            return nextFloat;
        }
        /// <summary> Returns a 32 bit uniformly distributed random number in the closed interval <tt>[Integer.MIN_VALUE,Integer.MAX_VALUE]</tt> (including <tt>Integer.MIN_VALUE</tt> and <tt>Integer.MAX_VALUE</tt>);</summary>
        public abstract int nextInt();
        /// <summary> Returns a 64 bit uniformly distributed random number in the closed interval <tt>[Long.MIN_VALUE,Long.MAX_VALUE]</tt> (including <tt>Long.MIN_VALUE</tt> and <tt>Long.MAX_VALUE</tt>).</summary>
        public virtual long nextLong()
        {
            // concatenate two 32-bit strings into one 64-bit string
            return ((nextInt() & unchecked((int)0xFFFFFFFFL)) << 32) | ((nextInt() & unchecked((int)0xFFFFFFFFL)));
        }
        /// <summary> Returns a 32 bit uniformly distributed random number in the open unit interval <code>(0.0,1.0)</code> (excluding 0.0 and 1.0).</summary>
        public virtual double raw()
        {
            int nextInt;
            do
            {
                // accept anything but zero
                nextInt = this.nextInt(); // in [Integer.MIN_VALUE,Integer.MAX_VALUE]-interval
            }
            while (nextInt == 0);

            // transform to (0.0,1.0)-interval
            // 2.3283064365386963E-10 == 1.0 / Math.pow(2,32)
            return (double)(nextInt & unchecked((int)0xFFFFFFFFL)) * 2.3283064365386963e-10;

            /*
            nextInt == Integer.MAX_VALUE   --> 0.49999999976716936
            nextInt == Integer.MIN_VALUE   --> 0.5
            nextInt == Integer.MAX_VALUE-1 --> 0.4999999995343387
            nextInt == Integer.MIN_VALUE+1 --> 0.5000000002328306
            nextInt == 1                   --> 2.3283064365386963E-10
            nextInt == -1                  --> 0.9999999997671694
            nextInt == 2                   --> 4.6566128730773926E-10
            nextInt == -2                  --> 0.9999999995343387
            */
        }
    }
}
